package com.utilities.parsers.gbkFileParser;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.LinkedList;

import com.utilities.parsers.gbkFileParser.ParseEvents.EventTypes;

public class ParseGBKFileParser   
{
	private String featureName = null;
	private String positionalInfo = null;
	private String featureRef = null;
	private EventTypes type = null;
	private RandomAccessFile accessFile = null;
	private long seqPosition = 0l;
	private static long offSet = 76;
	private boolean stopReading = false;
	
	private LinkedList<ParseListener> eventListeners = new LinkedList<ParseListener>();
	
	public void addListener(ParseListener parseListener)
	{
		eventListeners.add(parseListener);
	}
	
	private void fireEvent(ParseEvents parseEvent)
	{
		for (ParseListener listener : eventListeners)
		{
			listener.handleEvent(parseEvent);
		}
	}
	
	public void parse(File file) throws IOException
	{
		accessFile = new RandomAccessFile(file, "r");
		fireEvent(new ParseEvents(null,EventTypes.docStarted));
		String readLine = accessFile.readLine();
		while(readLine != null && !stopReading)
		{
			readLine = readLine.trim();
			if (readLine.startsWith("gene  "))
			{
				type = EventTypes.gene;
				String[] params = readLine.split(" ");
				positionalInfo = params[params.length - 1]; 
				featureName = accessFile.readLine().trim().split("=")[1].replaceAll("\"", "");
				//System.out.println(featureName);
				while(true)
				{
					readLine = accessFile.readLine().trim();
					if (readLine.startsWith("/db_xref="))
					{
						featureRef = readLine.split("=")[1].replaceAll("\"", "");
						break;
					}
				}
				fireEvent(new ParseEvents(readLine, type));
			}
			else if (readLine.startsWith("CDS  "))
			{
				type = EventTypes.cds;
				String[] params = readLine.split(" ");
				positionalInfo = params[params.length - 1]; 
				String str = accessFile.readLine();
				featureName = str.trim().split("=")[1].replaceAll("\"", "");
				//System.out.println(featureName);
				while(true)
				{
					readLine = accessFile.readLine().trim();
					if (readLine.startsWith("/db_xref="))
					{
						featureRef = readLine.split("=")[1].replaceAll("\"", "");
						break;
					}
				}
				fireEvent(new ParseEvents(readLine, type));
			}
			else if (readLine.startsWith("tRNA  "))
			{
				type = EventTypes.tRNA;
				String[] params = readLine.split(" ");
				positionalInfo = params[params.length - 1]; 
				featureName = accessFile.readLine().trim().split("=")[1].replaceAll("\"", "");
				while(true)
				{
					readLine = accessFile.readLine().trim();
					if (readLine.startsWith("/db_xref="))
					{
						featureRef = readLine.split("=")[1].replaceAll("\"", "");
						break;
					}
				}
				fireEvent(new ParseEvents(readLine, type));
			}
			else if (readLine.startsWith("rRNA  "))
			{
				type = EventTypes.rRNA;
				String[] params = readLine.split(" ");
				positionalInfo = params[params.length - 1]; 
				featureName = accessFile.readLine().trim().split("=")[1].replaceAll("\"", "");
				while(true)
				{
					readLine = accessFile.readLine().trim();
					if (readLine.startsWith("/db_xref="))
					{
						featureRef = readLine.split("=")[1].replaceAll("\"", "");
						break;
					}
				}
				fireEvent(new ParseEvents(readLine, type));
			}
			else if (readLine.startsWith("ncRNA  "))
			{
				type = EventTypes.ncRNA;
				String[] params = readLine.split(" ");
				positionalInfo = params[params.length - 1]; 
				featureName = null;
				featureRef = null;
				fireEvent(new ParseEvents(readLine, type));
			}
			else if (readLine.startsWith("tmRNA  "))
			{
				type = EventTypes.tmRNA;
				String[] params = readLine.split(" ");
				positionalInfo = params[params.length - 1]; 
				featureName = null;
				featureRef = null;
				fireEvent(new ParseEvents(readLine, type));
			}
			else if (readLine.startsWith("ORIGIN"))
			{
				type = EventTypes.sequenceStart;
				seqPosition = accessFile.getChannel().position();
				fireEvent(new ParseEvents(readLine, type));
//				break;
			}
			else if (readLine.startsWith("ACCESSION"))
			{
				type = EventTypes.accessionNumber;
				fireEvent(new ParseEvents(readLine,type));
			}
			else if (type == EventTypes.sequenceStart || type == EventTypes.sequence)
			{
				type = EventTypes.sequence;
				fireEvent(new ParseEvents(readLine, type));
			}
			readLine = accessFile.readLine();
		}
		fireEvent(new ParseEvents(null,EventTypes.docEnded));
	}
	
	public String getGeneAtPosition(long startPos, long endPos) throws IOException
	{
		StringBuffer buffer = new StringBuffer();
		long startlinenumber = 0l;
		long endlinenumber = 0l;
		if (startPos % 60 == 0) startlinenumber = (startPos / 60) - 1 ;
		else startlinenumber = startPos / 60;
		if (endPos % 60 == 0) endlinenumber = (endPos / 60) - 1 ;
		else endlinenumber = endPos / 60;
		
		accessFile.getChannel().position(seqPosition + startlinenumber * offSet);
		
		for (long j = startlinenumber; j <= endlinenumber ; j++)
		{
			StringBuffer temp = new StringBuffer();
			String readLine = accessFile.readLine().trim();
//			System.out.println(readLine);
			String[] params = readLine.split(" ");
			for (int i = 1; i< params.length ; i++)
			{
				temp.append(params[i]);
			}
			long startlineOffSet = startPos % 60;
			if (startlineOffSet == 0)
				startlineOffSet = 60;
			long endlineOffSet = 60;
			if (j == endlinenumber)
			{
				endlineOffSet = endPos % 60;
				if (endlineOffSet == 0)
					endlineOffSet = 60;
			}
			if (startlineOffSet > endlineOffSet)
			{
				System.out.println(readLine);
				System.exit(1);
			}
			buffer.append(temp.substring((int)startlineOffSet - 1, (int)endlineOffSet));
			startPos = startPos + (60 - startlineOffSet) + 1;
		}
		
		return buffer.toString();
	}
	
	public String getFeatureName()
	{
		return featureName;
	}
	
	public String getPositionalInfo()
	{
		return positionalInfo;
	}
	
	public String getFeatureRef()
	{
		return featureRef;
	}
	
	public void setStopReading(boolean stopReading)
	{
		this.stopReading = stopReading;
	}
}
